/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package grafox.Domain;

import grafox.Interface.ActionOnPrint;
import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;

/**
 * Esta classe implementa um Grafo, tendo como padrão não-poderado e direcional
 *
 */
public class GrafoX extends ObjectX {

    public GrafoX(long id, String nome, boolean isDirecional, boolean isPonderado) {
        super(id, nome);
        init();
        this.direcional = isDirecional;
        this.ponderado = isPonderado;
    }

    public GrafoX(long id, String nome, boolean isDirecional) {
        super(id, nome);
        init();
        this.direcional = isDirecional;
    }

    public GrafoX(String nome, boolean isDirecional) {
        super(nome);
        init();
        this.direcional = isDirecional;
    }

    private void init() {
        vertices = new Hashtable<>();
        arestas = new LinkedList<>();
    }
    private boolean direcional;

    /**
     *
     * @return Retorna se o grafo é direcional ou não
     */
    public boolean isDirecional() {
        return direcional;
    }
    public boolean ponderado;

    public boolean isPonderado() {
        return ponderado;
    }

    public VerticeX[] getVertices() {
        return (VerticeX[]) vertices.values().toArray();
    }

    public VerticeX getVertice(long idVertice) {
        return vertices.get(id);
    }

    public boolean existsVertice(long idVertice) {
        return vertices.get(id) != null;
    }

    public VerticeX addVertice(String nome, List<Object> propriedades) {
        VerticeX v = new VerticeX(nome);
        v.setGrafo(this);
        if (propriedades != null) {
            v.addAllPropriedades(propriedades);;
        }

        return v;

    }

    public VerticeX addVertice(long id, String nome, List<Object> propriedades) {
        VerticeX v = new VerticeX(id, nome);
        v.setGrafo(this);
        if (propriedades != null) {
            v.addAllPropriedades(propriedades);;
        }
        this.vertices.put(v.id, v);

        return v;

    }

    public VerticeX addVertice(VerticeX v) {
        assert v != null;
        v.setGrafo(this);

        this.vertices.put(v.id, v);

        return v;

    }

    public boolean existsAresta(ArestaX a) {
        return Collections.binarySearch(arestas, a) >= 0;

    }

    public ArestaX[] getArestas() {

        return (ArestaX[]) arestas.toArray();
    }

    public ArestaX addAresta(long id, String nome, long idOrigem, long idDestino, Double peso) {

        VerticeX vOrigem = vertices.get(idOrigem);
        VerticeX vDestino = vertices.get(idDestino);
        ArestaX aresta = null;

        if (vOrigem != null && vDestino != null) {
            if (peso == null) {
                aresta = new ArestaX(id, nome, vOrigem, vDestino, this.direcional);
            } else {
                aresta = new ArestaXPonderada(id, nome, vOrigem, vDestino, peso, this.direcional);
            }

            vOrigem.addAdjacente(aresta);
            if (!direcional) {
                vDestino.addAdjacente(aresta);
            }
            this.arestas.add(aresta);
        } else {
            throw new NoSuchElementException("Vertice de origem ou detino não existe neste grafo!");

        }

        validaAresta(aresta);
        aresta.setGrafo(this);
        return aresta;


    }

    public ArestaX addAresta(long id, String nome, VerticeX vOrigem, VerticeX vDestino, Double peso) {


        ArestaX aresta = null;

        if (vOrigem != null && vDestino != null && existsVertice(vOrigem.id) && existsVertice(vDestino.id)) {

            if (peso == null) {
                aresta = new ArestaX(id, nome, vOrigem, vDestino, this.direcional);
            } else {
                aresta = new ArestaXPonderada(id, nome, vOrigem, vDestino, peso, this.direcional);
            }



            vOrigem.addAdjacente(aresta);
            if (!direcional) {
                vDestino.addAdjacente(aresta);
            }
            this.arestas.add(aresta);
            validaAresta(aresta);
            aresta.setGrafo(this);
        }else {
            throw new NoSuchElementException("Vertice de origem ou detino não existe neste grafo!");

        }



        return aresta;
    }

    private boolean validaAresta(ObjectX obj) {
        if (this.ponderado) {
            if (!(obj instanceof ArestaXPonderada)) {
                throw new ClassCastException("Este grafo é ponderado logo somente aceita ArestaXPonderada");
            }
        }

        return true;


    }
    protected Hashtable<Long, VerticeX> vertices;
    protected List<ArestaX> arestas;

    public void Print() {
        for (Object object : propriedades) {
            if (object instanceof ActionOnPrint) {
                ((ActionOnPrint) object).execute(this);
            }
        }

    }
}
